﻿using ETW.EventListeners.ObjectValues;

namespace ETW.EventListeners.Listeners
{
    /// <summary>
    /// Http事件监听器
    /// System.Net.Http
    /// <para>
    /// 该监听器中的计数器由 HTTP 堆栈发布。
    /// </para>
    /// <para>
    /// https://docs.microsoft.com/zh-cn/dotnet/core/diagnostics/available-counters#systemnethttp-counters
    /// </para>
    /// </summary>
    public class HttpEventListenner : EventListener
    {
        #region 事件
        /// <summary>
        /// 当计数器写入 自进程启动以来启动的请求数  时触发
        /// <para>Requests Started (requests-started)</para>
        /// </summary>
        public event Action<SumCounterPayload, EventWrittenEventArgs> OnRequestsStartedWritten;
        /// <summary>
        /// 当计数器写入 每个更新间隔启动的请求数  时触发
        /// <para>Requests Started Rate (requests-started-rate)</para>
        /// </summary>
        public event Action<MeanCounterPayload, EventWrittenEventArgs> OnRequestsStartedRateWritten;
        /// <summary>
        /// 当计数器写入 自进程启动以来失败的请求数  时触发
        /// <para>Requests Failed (requests-failed)</para>
        /// </summary>
        public event Action<SumCounterPayload, EventWrittenEventArgs> OnRequestsFailedWritten;
        /// <summary>
        /// 当计数器写入 每个更新间隔的失败请求数  时触发
        /// <para>Requests Failed Rate (requests-failed-rate)</para>
        /// </summary>
        public event Action<MeanCounterPayload, EventWrittenEventArgs> OnRequestsFailedRateWritten;
        /// <summary>
        /// 当计数器写入 当前已启动但尚未完成或失败的活动 HTTP 请求数  时触发
        /// <para>Current Requests (current-requests)</para>
        /// </summary>
        public event Action<MeanCounterPayload, EventWrittenEventArgs> OnCurrentRequestsWritten;
        /// <summary>
        /// 当计数器写入 当前已启动但尚未完成或失败的 HTTP 1.1 连接数  时触发
        /// <para>Current HTTP 1.1 Connections (http11-connections-current-total)</para>
        /// </summary>
        public event Action<SumCounterPayload, EventWrittenEventArgs> OnHttp11ConnectionsCurrentTotalWritten;
        /// <summary>
        /// 当计数器写入 当前已启动但尚未完成或失败的 HTTP 2.0连接数  时触发
        /// <para>Current HTTP 2.0 Connections (http20-connections-current-total)</para>
        /// </summary>
        public event Action<SumCounterPayload, EventWrittenEventArgs> OnHttp20ConnectionsCurrentTotalWritten;
        /// <summary>
        /// 当计数器写入 HTTP 1.1 请求在请求队列中花费的平均持续时间  时触发
        /// <para>HTTP 1.1 Requests Queue Duration (http11-requests-queue-duration)</para>
        /// </summary>
        public event Action<MeanCounterPayload, EventWrittenEventArgs> OnHttp11RequestsqueueDurationWritten;
        /// <summary>
        /// 当计数器写入 HTTP 2.0 请求在请求队列中花费的平均持续时间  时触发
        /// <para>HTTP 2.0Requests Queue Duration (http20-requests-queue-duration)</para>
        /// </summary>
        public event Action<MeanCounterPayload, EventWrittenEventArgs> OnHttp20RequestsqueueDurationWritten;
        #endregion

        public HttpEventListenner() { }

        protected override void OnEventSourceCreated(EventSource eventSource)
        {
            if (!"System.Net.Http".Equals(eventSource.Name)) return;

            EnableEvents(eventSource, DefaultConfiguration.HttpEventListenerOptions.EventLevel, DefaultConfiguration.HttpEventListenerOptions.EventKeywords, new Dictionary<string, string>() { ["EventCounterIntervalSec"] = DefaultConfiguration.HttpEventListenerOptions.EventCounterIntervalSec.ToString() });
        }

        protected override void OnEventWritten(EventWrittenEventArgs eventData)
        {
            if (!"EventCounters".Equals(eventData.EventName)) return;

            if (!"System.Net.Http".Equals(eventData.EventSource.Name)) return;

            for (var i = 0; i < eventData.Payload.Count; i++)
            {
                if (eventData.Payload[i] is IDictionary<string, object> eventPayload)
                {
                    var counterName = GetName(eventPayload);

                    MeanCounterPayload meanCounterPayload = null;
                    SumCounterPayload sumCounterPayload = null;
                    var CounterType = eventPayload["CounterType"].ToString();

                    if (CounterType.Equals("Mean"))
                        meanCounterPayload = MeanCounterPayload.ConvertFrom(eventPayload);
                    else if (CounterType.Equals("Sum"))
                        sumCounterPayload = SumCounterPayload.ConvertFrom(eventPayload);
                    else continue;

                    switch (counterName)
                    {
                        case "requests-started":
                            if (OnRequestsStartedWritten != null)
                                OnRequestsStartedWritten.Invoke(sumCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnRequestsStartedWritten.Invoke(sumCounterPayload, eventData));
                            break;
                        case "requests-started-rate":
                            if (OnRequestsStartedRateWritten != null)
                                OnRequestsStartedRateWritten.Invoke(meanCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnRequestsStartedRateWritten.Invoke(meanCounterPayload, eventData));
                            break;
                        case "requests-failed":
                            if (OnRequestsFailedWritten != null)
                                OnRequestsFailedWritten.Invoke(sumCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnRequestsFailedWritten.Invoke(sumCounterPayload, eventData));
                            break;
                        case "requests-failed-rate":
                            if (OnRequestsFailedRateWritten != null)
                                OnRequestsFailedRateWritten.Invoke(meanCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnRequestsFailedRateWritten.Invoke(meanCounterPayload, eventData));
                            break;
                        case "current-requests":
                            if (OnCurrentRequestsWritten != null)
                                OnCurrentRequestsWritten.Invoke(meanCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnCurrentRequestsWritten.Invoke(sumCounterPayload, eventData));
                            break;
                        case "http11-connections-current-total":
                            if (OnHttp11ConnectionsCurrentTotalWritten != null)
                                OnHttp11ConnectionsCurrentTotalWritten.Invoke(sumCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnHttp11ConnectionsCurrentTotalWritten.Invoke(sumCounterPayload, eventData));
                            break;
                        case "http20-connections-current-total":
                            if (OnHttp20ConnectionsCurrentTotalWritten != null)
                                OnHttp20ConnectionsCurrentTotalWritten.Invoke(sumCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnHttp20ConnectionsCurrentTotalWritten.Invoke(sumCounterPayload, eventData));
                            break;
                        case "http11-requests-queue-duration":
                            if (OnHttp11RequestsqueueDurationWritten != null)
                                OnHttp11RequestsqueueDurationWritten.Invoke(meanCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnHttp11RequestsqueueDurationWritten.Invoke(meanCounterPayload, eventData));
                            break;
                        case "http20-requests-queue-duration":
                            if (OnHttp20RequestsqueueDurationWritten != null)
                                OnHttp20RequestsqueueDurationWritten.Invoke(meanCounterPayload, eventData);
                            //Task.Factory.StartNew(() => OnHttp20RequestsqueueDurationWritten.Invoke(meanCounterPayload, eventData));
                            break;
                    }
                }
            }
        }

        private static string GetName(IDictionary<string, object> eventPayload)
        {
            var counterName = "";
            if (eventPayload.TryGetValue("Name", out object displayValue))
            {
                counterName = displayValue.ToString();
            }

            return counterName;
        }
    }
}
